package cn.song.logread.config;


import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.song.logread.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * MVC通用配置
 *
 * @author SongRenShuo
 * @date 2025/01/05
 */
@Slf4j
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /** 登录超时时间  - 2小时*/
    private static final long LOGIN_TIMEOUT = 1000L * 60 * 60 * 2;
    /** IP 白名单 - 支持通配符 **/
    @Value("${ipWhitelistList}")
    private  String ipWhitelistList;
    /** 生产环境黑名单缓存  */
    private static List<String> ipWhitelistListCache = new ArrayList<>();

    /**
     * 添加侦听器
     *
     * @param registry 注册表
     * @date 2025/03/16
     *
     * @author SongRenShuo
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {


        InterceptorRegistration registration = registry.addInterceptor(new LogInterceptor(ipWhitelistList));

        List<String> statics = new ArrayList<>();
        statics.add("/");
        statics.add("/index");
        statics.add("/login");
        statics.add("/sys/login");
        statics.add("/static/**");

        // 放行静态资源
        registration.excludePathPatterns(statics);

        // 拦截所有接口请求
        registration.addPathPatterns("/**");
    }

    public static class LogInterceptor implements HandlerInterceptor {
        String ipWhitelistList;
        public LogInterceptor(String ipWhitelistList) {
            this.ipWhitelistList = ipWhitelistList;
        }

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            try {
                long startTime = System.currentTimeMillis(); // 记录请求开始时间
                String url = request.getRequestURI();
                String method = request.getMethod();
                String ip = IpUtil.getIpAddr(request);
                String queryString = JSONUtil.toJsonStr(request.getParameterMap());
                String token = request.getHeader("token");
                TokenManager tokenManager = TokenManager.getInstance();

                log.info("请求拦截 - URL: {}, Method: {}, IP: {},token:{}, Query: {}", url, method, ip, token, queryString);
                log.info("请求路径信息 - 完整URL: {}, URI: {}, ContextPath: {}, ServletPath: {}",
                        request.getRequestURL(),
                        request.getRequestURI(),
                        request.getContextPath(),
                        request.getServletPath());

                // 放行白名单与静态资源
                if (url.startsWith("/static/") || "/login".equals(url) || "/sys/login".equals(url)) {
                    return true;
                }
                // 判断是AJAX请求还是普通请求
                String requestedWith = request.getHeader("X-Requested-With");

                // token为空，返回401错误或重定向到登录页
                boolean whetherItIsAjax = "XMLHttpRequest".equals(requestedWith);

                if (token == null || token.isEmpty()) {
                    log.error("未检测到token，拦截请求 - URL: {}", url);
                    processingLogic(response, whetherItIsAjax, "未登录或登录已过期");
                    return false;
                }

                if (!tokenManager.isValidToken(token)) {
                    log.error("未登录 拦截到登录页 - URL: {}, Method: {}, IP: {},token:{}, Query: {}", url, method, ip, token, queryString);
                    processingLogic(response, whetherItIsAjax, "未登录或登录已过期");
                    return false;
                }

                long timeMillis = System.currentTimeMillis();
                long updatedTime = Convert.toLong(tokenManager.getTokenJwt(token).get("updatedTime"));
                if (timeMillis - updatedTime > LOGIN_TIMEOUT) {
                    log.error("登录超时 - URL: {}, Method: {}, IP: {},token:{}, Query: {}", url, method, ip, token, queryString);
                    processingLogic(response, whetherItIsAjax, "登录已过期，请重新登录");
                    return false;
                }
                if (!ipWhitelistVerify(ip,ipWhitelistList)) {
                    log.error("请求IP不可信 拦截到登录页 - URL: {}, Method: {}, IP: {},token:{}, Query: {}", url, method, ip, token, queryString);
                    processingLogic(response, whetherItIsAjax, "请求IP不可信："+ip);
                    return false;
                }

                tokenManager.updateTokenTime(token);
                // 将开始时间存入请求属性中
                request.setAttribute("startTime", startTime);
                return true;
            } catch (Exception e) {
                log.error("拦截器异常", e);
                return false;
            }
        }


        /**
         * 完成后
         *
         * @param request 请求
         * @param response 响应
         * @param handler 处理器
         * @param ex 前任
         * @date 2025/03/16
         *
         * @author SongRenShuo
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            // 获取开始时间
            long startTime = (Long) request.getAttribute("startTime");
            if (startTime != 0) {
                // 记录请求结束时间
                long endTime = System.currentTimeMillis();
                // 计算耗时
                long duration = endTime - startTime;
                String url = request.getRequestURI();
                String method = request.getMethod();
                String ip = request.getRemoteAddr();
                String queryString = JSONUtil.toJsonStr(request.getParameterMap());
                String token = request.getHeader("token");

                log.info("请求完成 - URL: {}, Method: {}, IP: {},token:{}, Query: {}, 耗时: {}ms", url, method, ip, token, queryString, duration);
            }
        }
    }

    /**
     * 添加静态资源文件
     *
     * @param registry 注册表
     * @date 2025/03/20
     *
     * @author SongRenShuo
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }

    /**
     * 处理逻辑
     *
     * @param whetherItIsAjax 是否为 Ajax 请求
     * @param msg 提示信息
     * @date 2025/03/21
     *
     * @author SongRenShuo
     */
    private static void processingLogic(HttpServletResponse response, boolean whetherItIsAjax, String msg) throws IOException {
        if (whetherItIsAjax) {
            // AJAX请求返回JSON
            response.setContentType("application/json;charset=UTF-8");
            // 401状态码
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"code\":401,\"msg\":\"" + msg + "\"}");
        } else {
            // 普通请求重定向到登录页
            response.sendRedirect("/login");
        }
    }

    /**
     * IP 白名单验证
     *
     * @param ip IP 地址
     * @date 2025/03/21
     * @return boolean
     * @author SongRenShuo
     */
    public static boolean ipWhitelistVerify(String ip,String ipWhitelistList) {
        initializeIpWhitelistListCache(ipWhitelistList);
        return IpUtil.ipWhitelistVerify(ip, ipWhitelistListCache);
    }


    /**
     * 初始化IP白名单缓存
     */
    private static void initializeIpWhitelistListCache(String ipWhitelistList) {
        if (ipWhitelistListCache.isEmpty()) {
            String utf8Str = new String(ipWhitelistList.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            if (StrUtil.isBlank(utf8Str)){
                utf8Str = "[]";
            }
            ipWhitelistListCache = JSONUtil.toList(utf8Str, String.class);
        }
    }


}
